#include "frame_thread_pool.h"

#include "mysql_build.h"
#include "log.h"


int exitPipe[2]; // handler只能访问全局的数据

void handler(int signum) {
    printf("Parent got signal, signum = %d\n", signum);
    write(exitPipe[1], "1", 1);
}

// ./server 192.168.142.128 12345 3 mysql密码
// ./server 192.168.142.128 12345 3 ""
int main(int argc, char *argv[]) {
    ARGS_CHECK(argc, 5);
    pipe(exitPipe);

    pid_t pid = fork();
    if(pid != 0) {
        //printf("I am parent, pid = %d\n", getpid());
        close(exitPipe[0]); // 父进程关闭管道的读端
        signal(SIGUSR1, handler);
        wait(NULL);
        printf("Parent is going to exit!\n");
        exit(0);
    }
    //printf("I am child, pid = %d\n", getpid());
    close(exitPipe[1]);  // 子进程关闭写端

    // 建立数据库
    MYSQL *mysql = mysql_init(NULL);
    setupDatabase(mysql, argv[4]);

    // 建立用户空间，3期
    buildUserSpace();

    // 建线程池
    threadPool_t threadPool;
    int workerNumber = atoi(argv[3]);
    threadPoolInit(&threadPool, workerNumber, mysql);

    // 建子线程
    makeWorker(&threadPool);

    // tcp
    int sockfd = tcpInit(argv[1], argv[2]);
    // epoll
    int epfd = epoll_create(1);
    epollAdd(epfd, sockfd);
    epollAdd(epfd, exitPipe[0]);

    struct epoll_event readySet[1024];
    timeWheel_t tw;
    initTimeWheel(&tw);
    while(1) {
        int readyNum = epoll_wait(epfd, readySet, 1024, 1000);
        //printf("time 1\n");
        for(int i = 0; i < readyNum; i++) {
            if(readySet[i].data.fd == sockfd) {
                int netfd = accept(sockfd, NULL, NULL);

                // 登录 注册功能
                // 初始化useropinfo  mysql netfd addr 
                // ID uesrName 在Login得到
                // opName 在后续recv得到, 现在先为0
                userOpInfo_t useropinfo;  // 用户验证使用的结构体
                memset(&useropinfo, 0, sizeof(useropinfo));
                useropinfo.mysql = mysql;
                useropinfo.netfd = netfd;
                strcpy(useropinfo.addr, userSpace);


                // 收int 根据其判断 是第一次连接还是老用户
                int tokenExist = -1;
                int retSockfd = 1;
                retSockfd = recv(netfd, &tokenExist, sizeof(int), MSG_WAITALL);
                if(retSockfd == 0) {
                    close(netfd);
                    continue;
                }
                printf("tokenExist = %d\n", tokenExist);

                // -1  第一次连接
                if(tokenExist == -1) {
                    // 用户验证
                    retSockfd = usersLogin(&useropinfo);
                    if(retSockfd == -1) {
                        close(netfd);
                        continue;
                    }
                    //printf("usersLogin ok\n");

                    // 用得到的用户名计算hash值
                    char userHash[41] = {0};
                    strcpy(userHash, calculateSha1(useropinfo.userName));

                    // 发送hash值
                    send(netfd, userHash, sizeof(userHash), MSG_NOSIGNAL);
                    //printf("send userHash ok\n");
                    // netfd入监听池 时间池
                    epollAdd(epfd, netfd);
                    addClient(&tw, netfd);
                    //printf("epollAdd ok\n\n");
                }
                else if(tokenExist == 1) { // 1  老用户
                    // 收userName， token, 计算token_hash, 对比
                    char token[41] = {0};
                    tlvPacket_t tlv;

                    // 收userName
                    memset(&tlv, 0, sizeof(tlv));
                    retSockfd = recv(netfd, &tlv.type, sizeof(tlv.type), MSG_WAITALL);
                    if(retSockfd == 0) {
                        close(netfd);
                        continue;
                    }
                    retSockfd = recv(netfd, &tlv.length, sizeof(tlv.length), MSG_WAITALL);
                    if(retSockfd == 0) {
                        close(netfd);
                        continue;
                    }
                    retSockfd = recv(netfd, tlv.value, tlv.length, MSG_WAITALL);
                    if(retSockfd == 0) {
                        close(netfd);
                        continue;
                    }
                    strcpy(useropinfo.userName, tlv.value);

                    // 收token
                    retSockfd = recv(netfd, token, sizeof(token), MSG_WAITALL);
                    if(retSockfd == 0) {
                        close(netfd);
                        continue;
                    }

                    // 计算name_hash, 对比 并发送
                    char nameHash[41] = {0};
                    strcpy(nameHash, calculateSha1(useropinfo.userName));
                    int isSame = 1;
                    if(strcmp(nameHash, token) != 0) { // 不相当
                        isSame = 0;
                    }
                    else {  // 相等
                        isSame = 1;
                    }
                    send(netfd, &isSame, sizeof(int), MSG_NOSIGNAL);

                    // netfd入监听池 时间池
                    epollAdd(epfd, netfd);
                    addClient(&tw,netfd);
                }
            }
            else if(readySet[i].data.fd == exitPipe[0]) {
                printf("threadPool is going to exit!\n");

                pthread_mutex_lock(&threadPool.mutex);
                threadPool.exitFlag = 1;
                pthread_mutex_unlock(&threadPool.mutex);
                pthread_cond_broadcast(&threadPool.cond);

                for(int j = 0; j < workerNumber; j++) {
                    pthread_join(threadPool.tidArr.arr[j], NULL);
                }
                threadPoolFree(&threadPool);
                close(sockfd);

                printf("All threads have exited.\n");
                exit(1);
            }
            else { // 处理客户端登录后的请求
                int netfd = readySet[i].data.fd;
                printf("in command connect, netfd = %d\n", netfd);
                // 更新时间池
                updateClient(&tw, netfd);
                //printf("updateClient ok\n");
                // 初始化useropinfo  mysql netfd addr 
                // ID uesrName opName 在recv得到
                userOpInfo_t useropinfo;  
                memset(&useropinfo, 0, sizeof(useropinfo));
                useropinfo.mysql = mysql;
                useropinfo.netfd = netfd;
                strcpy(useropinfo.addr, userSpace);

                // 收userName， token, 计算token_hash, 对比
                tlvPacket_t tlv;
                int retNetfd = 1;
                //printf("dataStruct ready\n");
                // opName
                memset(&tlv, 0, sizeof(tlv));
                retNetfd = recv(netfd, &tlv.type, sizeof(tlv.type), MSG_WAITALL);
                if(retNetfd == 0) {
                    close(netfd);
                    continue;
                }
                //printf("1\n");
                retNetfd = recv(netfd, &tlv.length, sizeof(tlv.length), MSG_WAITALL);
                if(retNetfd == 0) {
                    close(netfd);
                    continue;
                }
                //printf("2\n");
                retNetfd = recv(netfd, tlv.value, tlv.length, MSG_WAITALL);
                // if(retNetfd == 0) {
                //     close(netfd);
                //     continue;
                // }
                //printf("3\n");
                strcpy(useropinfo.opName, tlv.value);
                //printf("useropinfo.opName = %s\n", useropinfo.opName);

                // 收userName
                memset(&tlv, 0, sizeof(tlv));
                retNetfd = recv(netfd, &tlv.type, sizeof(tlv.type), MSG_WAITALL);
                if(retNetfd == 0) {
                    close(netfd);
                    continue;
                }
                retNetfd = recv(netfd, &tlv.length, sizeof(tlv.length), MSG_WAITALL);
                if(retNetfd == 0) {
                    close(netfd);
                    continue;
                }
                retNetfd = recv(netfd, tlv.value, tlv.length, MSG_WAITALL);
                // if(retNetfd == 0) {
                //     close(netfd);
                //     continue;
                // }
                strcpy(useropinfo.userName, tlv.value);
                //printf("useropinfo.userName = %s\n", useropinfo.userName);

                // 收cHead
                memset(&tlv, 0, sizeof(tlv));
                retNetfd = recv(netfd, &tlv.type, sizeof(tlv.type), MSG_WAITALL);
                if(retNetfd == 0) {
                    close(netfd);
                    continue;
                }
                retNetfd = recv(netfd, &tlv.length, sizeof(tlv.length), MSG_WAITALL);
                if(retNetfd == 0) {
                    close(netfd);
                    continue;
                }
                retNetfd = recv(netfd, tlv.value, tlv.length, MSG_WAITALL);
                // if(retNetfd == 0) {
                //     close(netfd);
                //     continue;
                // }
                char cHead[1024] = {0};
                strcpy(cHead, tlv.value);
                useropinfo.ID = pathToId(mysql, cHead);
                //printf("useropinfo.ID = %d\n", useropinfo.ID);

                // 收token
                char token[41] = {0};
                retNetfd = recv(netfd, token, sizeof(token), MSG_WAITALL);
                if(retNetfd == 0) {
                    close(netfd);
                    continue;
                }
                //printf("token = %s\n", token);

                printf("command: %d,  file: %s\n", tlv.type, useropinfo.opName);

                if(tlv.type == PUTS || tlv.type == GETS) { // gets puts 需要线程单独连接
                    pthread_mutex_lock(&threadPool.mutex);

                    printf("puts or gets file, netfd = %d give to thread\n", netfd);

                    // 调整线程所需资源， 通过 任务队列 与 线程池 传进线程
                    enQueue(&threadPool.taskQueue, netfd);
                    threadPool.ID = useropinfo.ID;
                    strcpy(threadPool.userName, useropinfo.userName);
                    strcpy(threadPool.opName, useropinfo.opName);
                    threadPool.type = tlv.type;

                    pthread_mutex_unlock(&threadPool.mutex);
                    pthread_cond_broadcast(&threadPool.cond);

                    // 移出 监听池
                    epollDel(epfd, netfd);
                }  
                else { // 其他命令主线程处理
                    retNetfd = dealCommand(&useropinfo, &tlv);  
                    if(retNetfd == -1) {
                        printf("Error! waiting for next command\n\n");
                        continue;
                    }
                    printf("\n");
                }

            }
        }
        //print_time_wheel(&tw);
        checkEcpiredClients(&tw, epfd);
    }

    mysql_close(mysql);
    close(sockfd);

    return 0;
}
